package com.example.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Parent {
    public void sayHello(){
        System.out.println("Hello Parent");
    }

    public void sayByeBye(){
        System.out.println("Bye bye");
    }

    private void privateMethodOfParent(){}
}

class Son extends Parent{

    @Override
    public void sayHello() {
        System.out.println("Hello Son");
    }

    private void privateMethodOfSon(){}
}
public class MethodTests {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // class.getDeclaredMethods()方法返回一个包含Method对象的数组，
        // 该对象反映此Class对象所表示的类或接口的所有声明方法，包括公共、受保护、默认（包）访问和私有方法，『但不包括继承的方法!』
        Method[] declaredMethods = Son.class.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(String.format("方法「%s」declaringClass:「%s」",
                    declaredMethod.getName(),
                    declaredMethod.getDeclaringClass()));
        }

        System.out.println("------------------------------");

        // class.getMethods()返回一个包含Method对象的数组，该对象反映此Class对象所表示的类或接口的所有『公共方法』，
        // 包括由类或接口声明的方法以及从超类和超接口继承的方法。
        for (Method method : Son.class.getMethods()) {
            System.out.println(String.format("方法「%s」declaringClass:「%s」",
                    method.getName(),
                    method.getDeclaringClass()));
        }

        System.out.println("------------------------------");
        Method sayHelloM1 = Son.class.getDeclaredMethod("sayHello");
        Method sayHelloM2 = Son.class.getDeclaredMethod("sayHello");
        // 这证明了两次获取同一方法对应的Method对象，获取到的是不同的Method对象实例
        System.out.println(sayHelloM1 == sayHelloM2); // false

        // 会抛出异常：java.lang.IllegalArgumentException: object is not an instance of declaring class
        // 虽然父类中也有一个名为sayHello的方法，但是通过子类的class拿到的是子类的方法而不是父类的方法，所以使用父类的实例去调用它的话会抛异常
        // sayHelloM1.invoke(new Parent());

        sayHelloM1.invoke(new Son()); // Hello Son

        Method sayHelloM3 = Parent.class.getDeclaredMethod("sayHello");
        sayHelloM3.invoke(new Parent()); // Hello Parent

        // 虽然sayHelloM3是通过父类获取的，但是使用子类的实例去调用它的时候，
        // 由于子类重写了这个方法，所以依旧表现出了正确的多态行为，也就是最终调用的是子类中重写的方法
        sayHelloM3.invoke(new Son()); // Hello Son
    }
}
